package no.rodland.acc.action;

import org.apache.log4j.Logger;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import no.rodland.acc.db.DbRow;
import no.rodland.acc.db.StoreService;
import no.rodland.acc.parsers.DbRowParser;
import no.rodland.acc.parsers.ParseException;

import com.opensymphony.xwork2.ActionSupport;

@SuppressWarnings({"UnusedDeclaration"})
public class UploadAction extends ActionSupport {
    private static final Logger log = Logger.getLogger(UploadAction.class);

    private File upload;
    private StoreService service;
    private List<DbRow> rows;
    private Set<DbRowParser> parsers;
    private DbRowParser parser;
    private List<String> parsingErrors;
    private int numLines;

    protected UploadAction() {
        // for testing
    }

    public UploadAction(Set<DbRowParser> parsers, StoreService service) {
        this.parsers = parsers;
        this.service = service;
    }

    public Set<DbRowParser> setParsers() {
        return parsers;
    }

    public Set<String> getParserNames() {
        Set<String> names = new HashSet<String>();
        for (DbRowParser parser : parsers) {
            names.add(parser.getName());
        }
        return names;
    }

    public void setParser(String key) {
        for (DbRowParser parser : parsers) {
            if (parser.getName().equals(key)) {
                this.parser = parser;
                return;
            }
        }
    }

    @Override
    public String execute() {
        parsingErrors = new ArrayList<String>();
        if (upload == null) {
            return "show";
        }

        rows = getRowsFromFile(upload);
        int inserted = service.insert(rows);
        log.info("inserted " + inserted + " rows in db (of " + rows.size() + ")");
        if (inserted != rows.size()) {
            addError("Some duplicates removed; had " + rows.size() + ", inserted " + inserted);
        }
        return SUCCESS;
    }

    @Override
    public void validate() {
        if (upload != null) {
            if (parser == null) {
                addActionError("We need a parser to parse the file.");
                addFieldError("dbRowParser", "We need a parser to parse the file.");
            }
        }
    }

    private List<DbRow> getRowsFromFile(File file) {
        List<DbRow> dbRows = new ArrayList<DbRow>();
        numLines = 0;

        try {
            FileInputStream fis = new FileInputStream(file);
            InputStreamReader isr = new InputStreamReader(fis, Charset.forName("ISO-8859-1"));
            BufferedReader input = new BufferedReader(isr);
            try {
                String line;
                while ((line = input.readLine()) != null) {
                    numLines++;

                    try {
                        List<DbRow> rows = parser.parse(line);
                        if (rows != null && rows.size() > 0) {
                            dbRows.addAll(rows);
                        }
                    }
                    catch (ParseException bpe) {
                        // addActionError("Not Parsable: "+ line);
                        addError(line);
                    }
                }
            }
            finally {
                input.close();
            }
        }
        catch (IOException ex) {
            ex.printStackTrace();
            addError(ex.getMessage());
        }
        log.info("Parsed " + numLines + " lines, and returned " + dbRows.size() + " db-rows.");
        return dbRows;
    }

    public File getUpload() {
        return upload;
    }

    public void setUpload(File upload) {
        this.upload = upload;
    }

    public List<DbRow> getRows() {
        Collections.sort(rows);
        return rows;
    }

    public int getSize() {
        return rows.size();
    }

    public int getLines() {
        return numLines;
    }

    private void addError(String line) {
        parsingErrors.add(line);
    }

    public List<String> getParsingErrors() {
        return parsingErrors;
    }
}